package com.alkalinesolutions.json.core.impl;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Suite;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Suite.SuiteClasses;

import com.alkalinesolutions.json.core.DepthExceededException;
import com.alkalinesolutions.json.core.JSONException;
import com.alkalinesolutions.json.core.JSONParser;
import com.alkalinesolutions.json.core.SyntaxException;

@RunWith(Suite.class)
@SuiteClasses({JSONTestSuite.FailingTests.class,
		JSONTestSuite.PassingTests.class})
@SuppressWarnings("nls")
public class JSONTestSuite {
	@RunWith(Parameterized.class)
	public static class FailingTests {
		@Parameters
		public static List<Object[]> data() {
			return Arrays.asList(
				new Object[] { "fail0.json", IllegalArgumentException.class },
				new Object[] { "fail1.json", JSONException.class },
				new Object[] { "fail2.json", SyntaxException.class },
				new Object[] { "fail3.json", SyntaxException.class },
				new Object[] { "fail4.json", SyntaxException.class },
				new Object[] { "fail5.json", SyntaxException.class },
				new Object[] { "fail6.json", SyntaxException.class },
				new Object[] { "fail7.json", SyntaxException.class },
				new Object[] { "fail8.json", SyntaxException.class },
				new Object[] { "fail9.json", SyntaxException.class },
				new Object[] { "fail10.json", SyntaxException.class },
				new Object[] { "fail11.json", SyntaxException.class },
				new Object[] { "fail12.json", SyntaxException.class },
				new Object[] { "fail13.json", SyntaxException.class },
				new Object[] { "fail14.json", SyntaxException.class },
				new Object[] { "fail15.json", SyntaxException.class },
				new Object[] { "fail16.json", SyntaxException.class },
				new Object[] { "fail17.json", SyntaxException.class },
				new Object[] { "fail18.json", DepthExceededException.class },
				new Object[] { "fail19.json", SyntaxException.class },
				new Object[] { "fail20.json", SyntaxException.class },
				new Object[] { "fail21.json", SyntaxException.class },
				new Object[] { "fail22.json", SyntaxException.class },
				new Object[] { "fail23.json", SyntaxException.class },
				new Object[] { "fail24.json", SyntaxException.class },
				new Object[] { "fail25.json", SyntaxException.class },
				new Object[] { "fail26.json", SyntaxException.class },
				new Object[] { "fail27.json", SyntaxException.class },
				new Object[] { "fail28.json", SyntaxException.class },
				new Object[] { "fail29.json", SyntaxException.class },
				new Object[] { "fail30.json", SyntaxException.class },
				new Object[] { "fail31.json", SyntaxException.class },
				new Object[] { "fail32.json", SyntaxException.class },
				new Object[] { "fail33.json", SyntaxException.class }
			);
		}

		private final String fileContents;
		private final JSONParser parser;
		private final Class<? extends Exception> expectedFailure;

		public FailingTests(
				final String fileName,
				final Class<? extends Exception> expectedFailure)
		throws IOException {
			this.expectedFailure = expectedFailure;
			final InputStream stream = JSONTestSuite.class.getResourceAsStream
				("/testsuite/" +fileName);
			if (stream == null)
				throw new IllegalArgumentException("test \"" + fileName + "\" missing");

			final InputStreamReader reader = new InputStreamReader(stream);
			final char data[] = new char[1024];
			final StringBuilder builder = new StringBuilder();
			int length;
			while ((length = reader.read(data)) > 0)
			{
				builder.append(data, 0, length);
			}
			parser = new JSONParser();
			fileContents = builder.toString();
		}

		@Test
		public void failures() {
			try {
				parser.parseText(fileContents);
			}
			catch (Exception e) {
				if ((e instanceof JSONException) && (e.getCause() != null)) {
					e = (Exception) e.getCause();
				}
				assertThat(e, instanceOf(expectedFailure));
			}
		}
	}
	@RunWith(Parameterized.class)
	public static class PassingTests {
		@Parameters
		public static List<Object[]> data() throws IOException {
			final ArrayList<Object[]> files = new ArrayList<Object[]>();
			for (int i = 0; i <= 3; i++)
			{
				final String fileName =  "/testsuite/pass" + i + ".json";
				final InputStream stream = JSONTestSuite.class.getResourceAsStream(fileName);
				if (stream == null) {
					throw new IllegalArgumentException("test \"" + fileName + "\" missing");
				}
				final InputStreamReader reader = new InputStreamReader(stream);
				final char data[] = new char[1024];
				final StringBuilder builder = new StringBuilder();
				int length;
				while ((length = reader.read(data)) > 0)
				{
					builder.append(data, 0, length);
				}
				final Object[] foo = { builder.toString() };
				files.add(foo);
			}
			return files;
		}

		private final String fileContents;
		private final JSONParser parser;

		public PassingTests(final String fileContents) {
			parser = new JSONParser();
			this.fileContents = fileContents;
		}

		@Test()
		public void passes() throws JSONException {
			final Object out = parser.parseText(fileContents);
			assertThat(out, notNullValue());
		}
	}}
